package com.acme.bankapp.service.app;

import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * My lock-free implementation of BlockingQueue. Only methods, required by
 * ThreadPoolExecutor, are implemented, others will throw UnsupportedOperationException.
 * 
 * @author Rostislav Kislenko
 *
 * @param <E> A type of data to be stored in this queue.
 */
public class MyLittleBlockingQueue<E> implements BlockingQueue<E> {
	private class Entry<T> {
		T data;
		AtomicReference<Entry<T>> next;
		public Entry(T data) {
			this.data = data;
			this.next = new AtomicReference<>(this);
		}
	}
	
	Entry<E> head;
	private int size;

	public MyLittleBlockingQueue() {
		head = new Entry<>(null);
	}
	
	@Override
	public E remove() {
		E returnValue = poll();
		if (returnValue == null)
			throw new NoSuchElementException();
		return returnValue;
	}

	@Override
	public E poll() {
		Entry<E> firstEntry = head.next.get();
		if (firstEntry == head) return null;
		while (!head.next.compareAndSet(firstEntry = head.next.get(), firstEntry.next.get())) {}
		--size;
		return firstEntry.data;
	}

	@Override
	public E element() {
		throw new UnsupportedOperationException();
	}

	@Override
	public E peek() {
		throw new UnsupportedOperationException();
	}

	@Override
	public int size() {
		return size;
	}

	@Override
	public boolean isEmpty() {
		return head.next.get() != head;
	}

	@Override
	public Iterator<E> iterator() {
		throw new UnsupportedOperationException();
	}

	@Override
	public Object[] toArray() {
		throw new UnsupportedOperationException();
	}

	@Override
	public <T> T[] toArray(T[] a) {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean containsAll(Collection<?> c) {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean addAll(Collection<? extends E> c) {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean removeAll(Collection<?> c) {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean retainAll(Collection<?> c) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void clear() {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean add(E e) {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean offer(E e) {
		Entry<E> newEntry = new Entry<>(e);
		Entry<E> firstEntry;
		while (true) {
			firstEntry = head.next.get();
			newEntry.next.set(firstEntry);
			if (head.next.compareAndSet(firstEntry, newEntry)) {
				++size;
				return true;
			}
			Thread.yield();
		}
	}

	@Override
	public void put(E e) throws InterruptedException {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean offer(E e, long timeout, TimeUnit unit)
			throws InterruptedException {
		throw new UnsupportedOperationException();
	}

	@Override
	public E take() throws InterruptedException {
		while (true) {
			Entry<E> firstEntry = head.next.get();
			if (firstEntry == head) {
				Thread.yield();
			} else {
				while (!head.next.compareAndSet(firstEntry = head.next.get(),
						firstEntry.next.get())) {}
				--size;
				return firstEntry.data;
			}
		}
	}

	@Override
	public E poll(long timeout, TimeUnit unit) throws InterruptedException {
		timeout = TimeUnit.NANOSECONDS.convert(timeout, unit);
		long finishTime = System.nanoTime() + timeout;
		while (System.nanoTime() > finishTime) {
			Entry<E> firstEntry = head.next.get();
			if (firstEntry == head) {
				Thread.yield();
			} else {
				while (!head.next.compareAndSet(firstEntry = head.next.get(),
						firstEntry.next.get())) {}
				--size;
				return firstEntry.data;
			}
		}
		return null;
	}

	@Override
	public int remainingCapacity() {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean remove(Object o) {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean contains(Object o) {
		throw new UnsupportedOperationException();
	}

	@Override
	public int drainTo(Collection<? super E> c) {
		throw new UnsupportedOperationException();
	}

	@Override
	public int drainTo(Collection<? super E> c, int maxElements) {
		throw new UnsupportedOperationException();
	}

}
